<?php

use Workerman\Worker;
use PHPSocketIO\SocketIO;

define('canvasWidth', 800);
define('canvasHeight', 600);
define('userR', 20);
define('fieldOffset', 30);

class Ball
{
    public $location = array();
    public $velocity = array();
    public $acceleration = array();
    private $frictionConstant = 0.05;
    public $r = 15;
    private $stopped = false;


    public function __construct()
    {
        $this->location = array('x' => canvasWidth / 2,
            'y' => canvasHeight / 2
        );
        $this->velocity = array('x' => 0,
            'y' => 0
        );
        $this->acceleration = array('x' => 0,
            'y' => 0
        );
        $this->r = 15;
        $this->frictionConstant = 0.05;
        $this->stopped = false;
    }

    public function update()
    {
        if (!$this->stopped) {
            $this->applyFriction();
            $this->velocity['x'] += $this->acceleration['x'];
            $this->velocity['y'] += $this->acceleration['y'];
            $this->location['x'] += $this->velocity['x'];
            $this->location['y'] += $this->velocity['y'];
            $this->acceleration['x'] = 0;
            $this->acceleration['y'] = 0;
        } else {
            $this->stopped = false;
        }
    }

    public function applyFriction()
    {
        $friction = array('x' => $this->velocity ['x'],
            'y' => $this->velocity ['y']);

        $friction ['x'] *= -1 * $this->frictionConstant;
        $friction ['y'] *= -1 * $this->frictionConstant;

        $this->applyForce($friction);
    }

    public function applyForce($force)
    {
        $this->acceleration['x'] += $force ['x'];
        $this->acceleration['y'] += $force ['y'];
    }

    public function stopBall()
    {
        $this->stopped = true;
        $this->acceleration ['x'] = 0;
        $this->acceleration ['y'] = 0;
        $this->velocity ['x'] = 0;
        $this->velocity ['y'] = 0;
    }
}

// composer autoload
require_once __DIR__ . '/../vendor/autoload.php';
$users = array();
$goal = array('x' => fieldOffset, 'y' => 150);
$ball = new Ball();
$isKicking = false;
$io = new SocketIO(8081);
$teams = array('red' => array('count' => 0, 'score' => 0), 'blue' => array('count' => 0, 'score' => 0));

function dist($x1, $y1, $x2, $y2)
{
    return sqrt(pow($x2 - $x1, 2) + pow($y2 - $y1, 2));
}

function mag($vector)
{
    return sqrt($vector['x'] * $vector['x'] + $vector['y'] * $vector['x']);
}

function normalize($vector)
{

    $m = mag($vector);
    if ($m > 0) {
        $vector['x'] /= $m;
        $vector['y'] /= $m;
    }
    return $vector;
}

function setMag($vector, $val)
{
    $vector = normalize($vector);
    $vector['x'] *= $val;
    $vector['y'] *= $val;
    return $vector;
}

function scored($team, $socket)
{
    global $teams, $ball;
    $teams[$team]['score']++;
    $socket->emit('scored', $teams);
    $ball->stopBall();
    $ball->location['x'] = canvasWidth / 2;
    $ball->location['y'] = canvasHeight / 2;
}

function ballEdges($socket)
{
    global $ball;
    // Check if outside the $GLOBALS['goal'] (y check) right-side
    if ($ball->location ['x'] + $ball->r > canvasWidth - fieldOffset && $ball->location ['y'] < canvasHeight / 2 - $GLOBALS['goal'] ['y'] / 2 || $ball->location ['x'] + $ball->r > canvasWidth - fieldOffset && $ball->location ['y'] > canvasHeight / 2 + $GLOBALS['goal'] ['y'] / 2) {
        $ball->location ['x'] = canvasWidth - $ball->r - fieldOffset;
        $ball->velocity ['x'] *= -1;

        // Check if inside the $GLOBALS['goal'] right-side && score team 1 (red)
    } else if ($ball->location ['x'] + $ball->r > canvasWidth && $ball->location ['y'] > canvasHeight / 2 - $GLOBALS['goal']['y'] / 2 || $ball->location ['x'] + $ball->r > canvasWidth && $ball->location ['y'] < canvasHeight / 2 + $GLOBALS['goal'] ['y'] / 2) {
        $ball->location ['x'] = canvasWidth - $ball->r;
        scored('red', $socket);
        // Check if outside the $GLOBALS['goal'] (y check) left-side
    } else if ($ball->location ['x'] - $ball->r < fieldOffset && $ball->location ['y'] < canvasHeight / 2 - $GLOBALS['goal']['y'] / 2 || $ball->location ['x'] - $ball->r < fieldOffset && $ball->location ['y'] > canvasHeight / 2 + $GLOBALS['goal'] ['y'] / 2) {
        $ball->location ['x'] = fieldOffset + $ball->r;
        $ball->velocity ['x'] *= -1;

        // Check if inside the $GLOBALS['goal'] left-side && score team 2 (blue)
    } else if ($ball->location ['x'] - $ball->r < 0 && $ball->location ['y'] > canvasHeight / 2 - $GLOBALS['goal']['y'] / 2 || $ball->location ['x'] - $ball->r < 0 && $ball->location ['y'] < canvasHeight / 2 + $GLOBALS['goal'] ['y'] / 2) {
        $ball->location ['x'] = $ball->r;
        scored('blue', $socket);
    }

    if ($ball->location ['y'] + $ball->r > canvasHeight) {
        $ball->velocity ['y'] *= -1;
        $ball->location ['y'] = canvasHeight - $ball->r;
    } else if ($ball->location ['y'] - $ball->r < 0) {
        $ball->velocity ['y'] *= -1;
        $ball->location ['y'] = $ball->r;
    }

}

function checkBallCollision()
{
    global $users, $ball, $isKicking;
    foreach ($users as $key => $user) {
        $dis = dist($user ['x'], $user ['y'], $ball->location ['x'], $ball->location ['y']);
        if ($dis <= userR + $ball->r) {
            $pushForce = array('x' => $ball->location['x'] - $user['x'], 'y' => $ball->location ['y'] - $user['y']);
            if ($isKicking) {
                $pushForce = setMag($pushForce, 15);
            } else {
                $pushForce = setMag($pushForce, 1.5);
                $user['isKicking'] = false;
            }
            $ball->applyForce($pushForce);
        }
    }
}

$io->on('connection', function ($socket) use ($io) {


    // when the client emits 'onLoad', this listens and executes
    $socket->on('onLoad', function ($user) use ($socket) {
        global $users, $teams;
        if (!in_array($user['id'], $users)) {
            $users[$socket->id] = $user;
            $team = $user['team'];
//            $username = $user['username'];
            $teams[$team]['count'] += 1;
            $socket->emit('onLoad', array(
                'teams' => $teams,
                'users' => $users,
            ));
//            $socket->broadcast->emit('user joined', array(
//                'team' => $team,
//                'username' => $username,
//                'numUsers' => count($users)
//            ));
        }
    });

    $socket->on('tick', function ($user) use ($socket) {
        global $users, $ball, $isKicking;
        $users[$socket->id] = $user;
        $isKicking = $user['isKicking'];
        checkBallCollision();
        ballEdges($socket);
        $ball->update();
        $socket->emit('tick', array('users' => $users, 'ballLoc' => $ball->location));
    });


    // when the user disconnects.. perform this
    $socket->on('disconnect', function ($id) use ($socket) {
        global $users, $teams;
        if (isset($users[$id])) {
            $user = $users[$id];
            $team = $user['team'];
            $username = $user['username'];
            $teams[$team]['count'] -= 1;
            unset($users[$id]);
            $socket->broadcast->emit('user left', array(
                'username' => $username,
                'numUsers' => count($users)
            ));
        }

    });
});

if (!defined('GLOBAL_START')) {
    Worker::runAll();
}
